Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix year calculation for the last day of a leap year. #1550

Merged
merged 1 commit into from
Dec 31, 2020

Conversation

BillyONeal
Copy link
Member

@BillyONeal BillyONeal commented Dec 31, 2020

calculate_year determines the year by following the gregorian calendar's rules of breaking years into 400, 100, and 4 year cycles. However, it has an edge case where the current year is the last day of a leap year, such that SecondsIn4Years contains that extra leap day but the calculation of year didn't account for the current date being exactly that extra day.

Since we expect anything in a 4 year cycle to be handled by SecondsIn4Years, if year ends up being 4, we know it is that last day of a leap year, and as such get the correct result by using 3 instead. The subsequent code that uses a month day table according to a year's leap-ness then handles the result correctly.

@barcharcraz
Copy link
Member

/azp run

@azure-pipelines
Copy link

No pipelines are associated with this pull request.

@barcharcraz barcharcraz merged commit fca7ac8 into microsoft:master Dec 31, 2020
@BillyONeal BillyONeal deleted the fix_leap_year_last_day branch December 31, 2020 23:11
Comment on lines +739 to +743
if (year1 == 4)
{
// this is the last day in a leap year
year1 = 3;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given this is being ported here, I want to understand it a bit better:
Azure/azure-sdk-for-cpp#1254

However, it has an edge case where the current year is the last day of a leap year, such that SecondsIn4Years contains that extra leap day but the calculation of year didn't account for the current date being exactly that extra day.

I don't fully follow what you mean here. Can you please elaborate, with a concrete example. Also, what do you mean by the "current year is the last day of a leap year"?

Lastly, what is an example impact of this bug exactly to an end user?

cc @antkmsft

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't fully follow what you mean here. Can you please elaborate, with a concrete example.

2020-12-31 results in year1 being 4, despite this code expecting all blocks of 4 years to be handled by SecondsIn4Years.

Also, what do you mean by the "current year is the last day of a leap year"?

Should be current "current day is the last day of a leap year"

Lastly, what is an example impact of this bug exactly to an end user?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lastly, what is an example impact of this bug exactly to an end user?

It incorrectly prints Thursday 2020-12-31 as Thu 2021-01-01

@@ -736,6 +736,12 @@ static compute_year_result compute_year(int64_t secondsSince1900)
int secondsInt = static_cast<int>(secondsLeft - year4 * SecondsIn4Years);

int year1 = secondsInt / SecondsInYear;
if (year1 == 4)
{
// this is the last day in a leap year
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment is a little confusing since we are talking about a day, but the variable is year.

@yangyud-cn
Copy link

Hi,
Your code is still broken, although in a tiny window, currently 5 seconds, because of leap seconds since 2001. https://en.wikipedia.org/wiki/Leap_second
Also in year 2100 (I'm pretty sure it will not be alive then :-) https://en.wikipedia.org/wiki/Leap_year

@BillyONeal
Copy link
Member Author

Your code is still broken, although in a tiny window, currently 5 seconds, because of leap seconds since 2001. https://en.wikipedia.org/wiki/Leap_second

Not processing leap seconds is intentional. The input is a unix timestamp, and unix time does not respect leap seconds.

If it did account for leap seconds that would break the consistency of timestamps; because leap seconds are not issued at a fixed interval, it would change the meaning of timestamps before or after they were added to any such accounting table or similar.

Also in year 2100 (I'm pretty sure it will not be alive then :-) https://en.wikipedia.org/wiki/Leap_year

I'm not seeing an obvious bug here related to the year 2100. The "leap years are those divisible by 4 but not divisible by 10" rule is why this is handled in 400 year cycles in the first place (and why the NT epoch is 1601).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants